#include "linkcontrol.h"

LinkControl::LinkControl(void)
{
  uint32_ClockCycle = 0;

  listener[0] = NONE;
  listener[1] = NONE;
  listener[2] = NONE;
  listener[3] = NONE;
  listener[4] = NONE;
  listener[5] = NONE;
  listener[6] = NONE;
  listener[7] = NONE;
  
  LocationList[0] = NORTH;
  LocationList[1] = NORTHEAST;
  LocationList[2] = NORTHWEST;
  LocationList[3] = SOUTH;
  LocationList[4] = SOUTHEAST;
  LocationList[5] = SOUTHWEST;
  LocationList[6] = EAST;
  LocationList[7] = WEST;

}

LinkControl::~LinkControl(void)
{
  /* destroying links, but not the physical link
   * physical link is destroyed in the linkslist */
  delete North;
  delete NorthEast;
  delete NorthWest;
  delete South;
  delete SouthWest;
  delete SouthEast;
  delete East;
  delete West;
}

void LinkControl::SetHostAddress(unsigned int hostAddress)
{
  uint32_HostAddress = hostAddress;

  North->SetTargetLabel(uint32_HostAddress);
  NorthEast->SetTargetLabel(uint32_HostAddress);
  NorthWest->SetTargetLabel(uint32_HostAddress);
  South->SetTargetLabel(uint32_HostAddress);
  SouthEast->SetTargetLabel(uint32_HostAddress);
  SouthWest->SetTargetLabel(uint32_HostAddress);
  East->SetTargetLabel(uint32_HostAddress);
  West->SetTargetLabel(uint32_HostAddress);
  
}

void LinkControl::SetLinkToNeighbor(LinkLocation location, bool * dataLine)
{
  Link * LinkBuff;

  /* create new link */
  LinkBuff = new Link(dataLine);

  switch(location)
  {
    case NORTH:
      North = LinkBuff;
      break;
    case NORTHEAST:
      NorthEast = LinkBuff;
      break;
    case NORTHWEST:
      NorthWest = LinkBuff;
      break;
    case SOUTH:
      South = LinkBuff;
      break;
    case SOUTHEAST:
      SouthEast = LinkBuff;
      break;
    case SOUTHWEST:
      SouthWest = LinkBuff;
      break;
    case EAST:
      East = LinkBuff;
      break;
    case WEST:
      West = LinkBuff;
      break;
    default:
      break;
  }
}

bool * LinkControl::GetPhysicalLinkAddress(LinkLocation location)
{
  Link * linkBuff;

  linkBuff = GetLink(location);

  return linkBuff->GetPhysicalLinkAddress();

}

void LinkControl::ClockLinkControl(void)
{
   /* Clock Links 
   *
   * Link currently requires 10 clock cycles to complete 1 communication cycle
   *
   * */

  if(uint32_ClockCycle < 10)
  {
    ClockLinks();
  }

  /* Cycle Through Links for Comparisons
   *
   * WIll do simple comparison with smallest value for beginning tests
   *
   * */

  if(10 == uint32_ClockCycle) LinkComparisons();

  /* Update the link's dependency on listening to other links
   *
   * This is how the links transfer data through
   * */
  if(11 == uint32_ClockCycle) UpdateLinkListeners();

  // reset clock
  if(uint32_ClockCycle < 12)
  {
    uint32_ClockCycle++;
  }
  else
  {
    uint32_ClockCycle = 0;
  }

}

void LinkControl::PrintLinksBuildLabel(void)
{
  std::cout << std::endl;
  std::cout << "Link Control For Host Processor: " << uint32_HostAddress << std::endl;

  std::cout << "North: " <<  North->ReadBuildLabel();
  std::cout << "\tNorth Label Bit Position: " << North-> ReadLabelBitPosition() << std::endl;
  
  std::cout << "NorthEast: " << NorthEast->ReadBuildLabel();
  std::cout << "\tNorthEast Label Bit Position: " << NorthEast->ReadLabelBitPosition() << std::endl;

  std::cout << "NorthWest: " << NorthWest->ReadBuildLabel();
  std::cout << "\tNorthWest Label Bit Position: " << NorthWest-> ReadLabelBitPosition() << std::endl;

  std::cout << "South: " << South->ReadBuildLabel();
  std::cout << "\tSouth Label Bit Position: " << South-> ReadLabelBitPosition() << std::endl;

  std::cout << "SouthEast: " << SouthEast->ReadBuildLabel();
  std::cout << "\tSouthEast Label Bit Position: " << SouthEast->ReadLabelBitPosition() << std::endl;
  
  std::cout << "SouthWest: " << SouthWest->ReadBuildLabel();
  std::cout << "\tSouthWest Label Bit Position: " << SouthWest-> ReadLabelBitPosition() << std::endl;

  std::cout << "East: " << East->ReadBuildLabel();
  std::cout << "\t\tEast Label Bit Position: " << East->ReadLabelBitPosition() << std::endl;

  std::cout << "West: " << West->ReadBuildLabel();
  std::cout << "\t\tWest Label Bit Position: " << West->ReadLabelBitPosition() << std::endl;

  std::cout << std::endl;
}

void LinkControl::ClockLinks(void)
{
  Link * linkBuff;

  for(int loop = 0; loop < 8; loop++)
  {

    linkBuff = GetLink(LocationList[loop]);
    if(NULL != linkBuff && linkBuff->IsLinkActive()) linkBuff->ClockLink();

  }

}

void LinkControl::LinkComparisons(void)
{
  LinkMinShift = 99;
  LinkMaxLabel = 0;
  Link * linkBuff;

/* get minimum shift length 
   * 
   * */
  for(int loop = 0; loop < 8; loop++)
  {
    linkBuff = GetLink(LocationList[loop]);

    if(linkBuff->IsLinkActive())
    {

      if(LinkMinShift > linkBuff->ReadLabelBitPosition())
      {
        LinkMinShift = linkBuff->ReadLabelBitPosition();
      }
    }
  }

  
  /* get maximum label
  *
  * */
  if(99 != LinkMinShift)
  {

    for(int loop = 0; loop < 8; loop++)
    {
      linkBuff = GetLink(LocationList[loop]);

      if(linkBuff->IsLinkActive())
      {
        if((linkBuff->ReadBuildLabel() >> (MAXBITLENGTH - LinkMinShift)) > LinkMaxLabel)
        {
          LinkMaxLabel = (linkBuff->ReadBuildLabel() >> (MAXBITLENGTH - LinkMinShift));
          CurrentMaxLink = LocationList[loop];
        }
      }

    }


    /* compare and reset individual links
     *
     * */
    for(int loop = 0; loop < 8; loop++)
    {
      linkBuff = GetLink(LocationList[loop]);


      if(linkBuff->IsLinkActive())
      {
        /* check to see if link label is greater than the global label */
        if(LinkMaxLabel > (linkBuff->ReadBuildLabel() >> (MAXBITLENGTH - LinkMinShift)))
        {
          linkBuff->SetLinkResetFromHost();
          listener[loop] = CurrentMaxLink;
        }
      }

    }

  } // end if(99 != LinkMinShift)
}

void LinkControl::UpdateLinkListeners(void)
{
  Link * linkToReadLabel;
  Link * linkBuff;

  for(int loop = 0; loop < 8; loop++)
  {
    linkBuff = GetLink(LocationList[loop]);

    if(linkBuff->IsLinkActive())
    {

      if(NONE != listener[loop])
      {
        linkToReadLabel = GetLink(listener[loop]);
        linkBuff->SetTargetLabel(linkToReadLabel->ReadBuildLabel());
      }

    }

  }

}

bool LinkControl::AreAllLinksComplete(void)
{
  bool allFinished = 1;
  unsigned int finalLabel = 99;
  Link * linkBuff;

  for(int loop = 0; loop < 8; loop++)
  {
    linkBuff = GetLink(LocationList[loop]);

    if(linkBuff->IsLinkActive())
    {

      if(linkBuff->IsLinkComplete())
      {
        allFinished &= 1;
        finalLabel = linkBuff->ReadBuildLabel();
      }
      else
      {
        allFinished = 0;
      }

    }

  }

  if(allFinished)
  {
    uint32_FinalLabel = finalLabel;
  }
  else
  {
    finalLabel = 0;
  }

  return allFinished;

}

unsigned int LinkControl::GetLinkControlFinalLabel(void)
{
  return uint32_FinalLabel;
}

void LinkControl::PrintActiveLinks(void)
{
  Link * linkBuff;

  for(int loop = 0; loop < 8; loop++)
  {
    linkBuff = GetLink(LocationList[loop]);

    if(linkBuff->IsLinkActive())
    {
      linkBuff->PrintLinkData();
    }
  }

}


/* NOTE: use this method for smarter synchromization of the links 
 * this is more of a phase 2 type implementation */
int LinkControl::CompareTwoNumbers(unsigned int valueA, unsigned int valueB)
{
  int returnValue = -99;

  if(valueA == valueB)
  {
    returnValue = 0;
  }
  else if(valueA < valueB)
  {
    returnValue = -1;
  }
  else if(valueA > valueB)
  {
    returnValue = 1;
  }

  return returnValue;
}

/* NOTE: use this method for smarter synchromization of the links 
 * this is more of a phase 2 type implementation */
void LinkControl::CompareTwoLinksBuildLabels(LinkLocation locationA, LinkLocation locationB)
{
  Link * LinkA = GetLink(locationA);
  Link * LinkB = GetLink(locationB);

  unsigned int BitPosition;
  unsigned int LinkACompareLabel;
  unsigned int LinkBCompareLabel;

  /* find the lesser of the two links bit position */
  switch(CompareTwoNumbers(LinkA->ReadLabelBitPosition() , LinkB->ReadLabelBitPosition()))
  {
    case 0:  /* equal */
      BitPosition = LinkA->ReadLabelBitPosition();
      break;
    case -1: /* A < B */
      BitPosition = LinkA->ReadLabelBitPosition();
      break;
    case 1:  /* B < A */
      BitPosition = LinkB->ReadLabelBitPosition();
      break;
    default:
      std::cout << "ERROR IN NUMBER COMPARISON" << std::endl;
      break;
  }

  /* TODO: Check to verify that the bit position is not a problem - (OFFSET) */
  LinkACompareLabel = (LinkA->ReadBuildLabel() >> (MAXBITLENGTH - BitPosition));
  LinkBCompareLabel = (LinkB->ReadBuildLabel() >> (MAXBITLENGTH - BitPosition));

  /* checking for link reset */
  switch(CompareTwoNumbers(LinkACompareLabel, LinkBCompareLabel))
  {
    case 0:  /* equal */
      /* Do Nothing */
      break;
    case -1: /* A < B */
      /* Reset A */
      LinkA->SetLinkResetFromHost();
      /* Set Target Label = B */
      LinkA->SetTargetLabel(LinkB->ReadTargetLabel());
      break;
    case 1:  /* B < A */
      /* Reset B */
      LinkB->SetLinkResetFromHost();
      /* Set Target Label = A */
      LinkB->SetTargetLabel(LinkA->ReadTargetLabel());
      break;
    default:
      std::cout << "ERROR IN NUMBER COMPARISON" << std::endl;
      break;
  }

  /* update link if target label is still being built */
  UpdateLinkTargetLabel(LinkA, LinkB);

}

/* NOTE: use this method for smarter synchromization of the links 
 * this is more of a phase 2 type implementation */
void LinkControl::UpdateLinkTargetLabel(Link * LinkA, Link * LinkB)
{

  /* find the lesser of the two links bit position */
  switch(CompareTwoNumbers(LinkA->ReadLabelBitPosition() , LinkB->ReadLabelBitPosition()))
  {
    case 0:  /* equal */
      /* do nothing */
      break;
    case -1: /* A < B */
      /* 
       * set linkA's target label against the build label of linkB
       * this allows linkA to dynamically target its label against
       * the label currently being built by linkB. This allows the 
       * system a faster response time to building the final label
       *
       * */
      LinkA->SetTargetLabel(LinkB->ReadBuildLabel());
      break;
    case 1:  /* B < A */
      /* 
       * same rules as case -1, just link roles are swapped
       *
       * */
      LinkB->SetTargetLabel(LinkA->ReadBuildLabel());
      break;
    default:
      std::cout << "ERROR IN NUMBER COMPARISON" << std::endl;
      break;
  }
}

Link * LinkControl::GetLink(LinkLocation location)
{
  Link * LinkBuff;

  switch(location)
  {
    case NORTH:
      LinkBuff = North;
      break;
    case NORTHEAST:
      LinkBuff = NorthEast;
      break;
    case NORTHWEST:
      LinkBuff = NorthWest;
      break;
    case SOUTH:
      LinkBuff = South;
      break;
    case SOUTHEAST:
      LinkBuff = SouthEast;
      break;
    case SOUTHWEST:
      LinkBuff = SouthWest;
      break;
    case EAST:
      LinkBuff = East;
      break;
    case WEST:
      LinkBuff = West;
      break;
    default:
      break;
  }
  return LinkBuff;
}
